import 'package:compact_dio/src/base_decoder.dart';
import 'package:compact_dio/src/compact_dio_config.dart';
import 'package:compact_dio/src/compact_dio_exception.dart';
import 'package:compact_dio/src/exception_handler.dart';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:dio/dio.dart';
import 'compact_dio_result.dart';

/// A class that handles HTTP requests using Dio library with compact configuration.
class CompactDioRequest {
  final CompactDioConfig config;
  final BaseDecoder decoder;
  final ExceptionHandler? exceptionHandler;

  Dio? _dio;

  /// Creates a new instance of [CompactDioRequest].
  ///
  /// The [config] parameter specifies the configuration for the Dio instance.
  /// The [decoder] parameter specifies the decoder used to decode the response.
  CompactDioRequest({
    required this.config,
    required this.decoder,
    this.exceptionHandler,
  }) {
    _dio = _createDio();
  }

  Dio _createDio() {
    final option = BaseOptions(
      baseUrl: config.baseUrl ?? '',
      connectTimeout: config.connectTimeout,
      receiveTimeout: config.receiveTimeout,
      sendTimeout: config.sendTimeout,
      headers: config.headers,
    );
    final dio = Dio(option);

    if (config.interceptors != null && config.interceptors!.isNotEmpty) {
      dio.interceptors.addAll(config.interceptors!);
    }
    return dio;
  }

  /// Sends an HTTP request and returns the result.
  ///
  /// The [method] parameter specifies the HTTP method to use.
  /// The [path] parameter specifies the path of the request.
  /// The [params] parameter specifies the query parameters of the request.
  /// The [data] parameter specifies the data to send with the request.
  /// The [baseUrl] parameter specifies the base URL to use for the request.
  /// The [decoder] parameter specifies the decoder used to decode the response.
  /// The [type] parameter specifies the type of the response.
  /// The [cancelToken] parameter specifies the token used to cancel the request.
  /// The [contentType] parameter specifies the content type of the request.
  ///
  /// Returns a [CompactDioResult] object that represents the result of the request.
  Future<CompactDioResult<R>> request<R, T>(
    Method method,
    String path, {
    Map<String, dynamic>? params,
    dynamic data,
    String? baseUrl,
    BaseDecoder? decoder,
    T? type,
    CancelToken? cancelToken,
    String? contentType,
    ProgressCallback? onSendProgress,
    ProgressCallback? onReceiveProgress,
  }) async {
    try {
      if (config.checkConnectivity ?? true) {
        var connectivityResult = await (Connectivity().checkConnectivity());
        if (connectivityResult == ConnectivityResult.none) {
          return CompactDioResult.error(
            -1,
            '无法连接网络,请检查网络设置',
          );
        }
      }
      if (baseUrl != null) _dio!.options.baseUrl = baseUrl;
      Response response = await _dio!.request(
        path,
        queryParameters: params,
        data: data,
        cancelToken: cancelToken,
        options: Options(
          method: _methodValues[method],
          contentType: contentType,
        ),
        onSendProgress: onSendProgress,
        onReceiveProgress: onReceiveProgress,
      );
      decoder ??= this.decoder;
      final result = decoder.decode(response: response, type: type);
      return CompactDioResult.success(result);
    } on DioException catch (e) {
      final code = e.response?.statusCode ?? -1;
      String message;
      if (exceptionHandler != null) {
        message = exceptionHandler!.handleException(e);
      } else {
        message = e.message ?? '未知错误';
      }
      return CompactDioResult.error(
        code,
        message,
      );
    } on CompactDioException catch (e) {
      return CompactDioResult.error(
        e.statusCode,
        e.message,
      );
    }
  }
}

/// An enumeration of HTTP methods.
enum Method { GET, POST, DELETE, PUT }

const _methodValues = {
  Method.GET: "get",
  Method.POST: "post",
  Method.DELETE: "delete",
  Method.PUT: "put",
};
